﻿using System.Collections.Generic;
using System.Data;
using System.Data.Common;

namespace DataScripter.Database
{
	public abstract class Db
	{
	    public string Instance { get; protected set; }
	    public string Database { get; protected set; }

	    protected Db(string instance, string database)
		{
			Instance = instance;
			Database = database;
		}

	    protected Db()
	    {
	        
	    }

	    //public abstract string GetConnectionstring();

	    protected abstract IDbConnection GetOpenConnection();
	    protected abstract DbDataAdapter CreateDataAdapter(IDbConnection connection, string query);

		public override string ToString()
		{
			return string.Format("[{0}] {1}", Instance, Database);
		}

        public DataSet ExecuteDataSet(string query)
        {
            DataSet dataSet = new DataSet();
            return ExecuteDataSet(query, dataSet);
        }

        public DataSet ExecuteDataSet(string query, DataSet destination)
        {
            using (IDbConnection connection = GetOpenConnection())
            {
                IDbDataAdapter adapter = CreateDataAdapter(connection, query);
                adapter.Fill(destination);
                connection.Close();
            }
            return destination;
        }

        public DataTable ExecuteToDataTable(string query)
        {
            DataTable dataTable = new DataTable();
            ExecuteToDataTable(query, dataTable);
            return dataTable;
        }

        public void ExecuteToDataTable(string query, DataTable dataTable)
        {
            using (IDbConnection connection = GetOpenConnection())
            {
                DbDataAdapter adapter = CreateDataAdapter(connection, query);
                adapter.Fill(dataTable);
                connection.Close();
            }
        }

        public void ExecuteNonQuery(string query)
        {
            using (IDbConnection connection = GetOpenConnection())
            using (IDbCommand command = connection.CreateCommand())
            {
                command.CommandText = query;
                command.ExecuteNonQuery();

            }
        }

        public IDataReader ExecuteReader(string query)
        {
            IDbConnection connection = GetOpenConnection();
            using (IDbCommand command = connection.CreateCommand())
            {
                command.CommandText = query;
                return command.ExecuteReader(CommandBehavior.CloseConnection);
            }
        }

        public TReturnValue ExecuteScalar<TReturnValue>(string query)
        {
            using (IDbConnection connection = GetOpenConnection())
            using (IDbCommand command = connection.CreateCommand())
            {
                command.CommandText = query;
                return (TReturnValue)command.ExecuteScalar();
            }
        }

        public int ExecuteCount(string tablename, string whereClause)
        {
            return ExecuteScalar<int>(string.Format("SELECT COUNT(*) FROM {0} WHERE {1}", tablename, whereClause));
        }

        public bool ExecuteExists(string tablename, string whereClause)
        {
            using (IDataReader reader = ExecuteReader(string.Format("SELECT TOP 1 1 FROM {0} WHERE {1}", tablename, whereClause)))
            {
                return reader.Read();
            }
        }

        public IEnumerable<T> ExecuteEnumerable<T>(string query)
        {
            using (IDataReader reader = ExecuteReader(query))
            {
                while (reader.Read())
                {
                    switch (typeof(T).FullName)
                    {
                        case "System.Guid":
                            object item = reader.GetGuid(0);
                            yield return (T)item;
                            break;
                        case "System.String":
                            object stringitem = reader.GetString(0);
                            yield return (T)stringitem;
                            break;
                    }
                }
            }
        }

        public IEnumerable<T> Execute<T>(string query) 
            where T : IMetadataReader, new()
        {
            using (IDataReader reader = ExecuteReader(query))
            {
                while (reader.Read())
                {
                    IMetadataReader t = new T();
                    t.InitializeMetadata(reader);
                    yield return (T)t;
                }
            }
        }

	}
}
